package dht.simulation;

import java.util.HashMap;
import java.util.TreeSet;

public class RouteTable implements Cloneable {
	// <index, macAddress>
	public TreeSet<VNode> table;
	public static final int ringLength = GlobalVariables.ringLength;

	public RouteTable() {
		table = new TreeSet<VNode>();
	}

	public String join(String macAddress, double joinTime) {
		if (table.size() == 0) {
			VNode vnode = new VNode(0, RouteTable.ringLength - 1, joinTime,
					macAddress);
			table.add(vnode);
			return null;
		}

		VNode maxLoadVNode = table.iterator().next();
		for (VNode vnode : table) {
			if ((vnode.getLoad(joinTime) > maxLoadVNode.getLoad(joinTime))
					|| (Math.abs(vnode.getLoad(joinTime)
							- maxLoadVNode.getLoad(joinTime)) < GlobalVariables.eps && (vnode.end
							- vnode.begin + ringLength)
							% ringLength > (maxLoadVNode.end
							- maxLoadVNode.begin + ringLength)
							% ringLength)) {
				maxLoadVNode = vnode;
			}
		}

		int end = (((maxLoadVNode.end - maxLoadVNode.begin + ringLength) % ringLength) / 2 + maxLoadVNode.begin)
				% ringLength;
		VNode splitNode = new VNode(maxLoadVNode.begin, end, joinTime,
				maxLoadVNode.macAddress);
		VNode newNode = new VNode(end + 1, maxLoadVNode.end, joinTime,
				macAddress);

		table.remove(maxLoadVNode);
		table.add(splitNode);
		table.add(newNode);

		return maxLoadVNode.macAddress;
	}

	public VNode lookup(String fileName) {
		int index = fileName.hashCode() % ringLength;
		VNode tmpNode = new VNode(index);
		VNode node = table.floor(tmpNode);
		if (node == null) {
			tmpNode.begin += ringLength;
			node = table.floor(tmpNode);
		}
		return node;
	}

	public void increateReadCounter(int value, VNode vnode) {
		vnode.readCounter += value;
		table.remove(vnode);
		table.add(vnode);
	}

	public void increateWriteCounter(int value, VNode vnode) {
		vnode.writeCounter += value;
		table.remove(vnode);
		table.add(vnode);
	}

	public void updateVNodeStatistics(TreeSet<VNode> serverTable,
			String serverMacAddress) throws DhtException {
		for (VNode serverNode : serverTable) {
			if (serverNode.macAddress.equals(serverMacAddress)) {
				if (!table.contains(serverNode)) {
					throw new DhtException(
							"updateVNodeStatistics info don't match");
				}
				table.remove(serverNode);
				table.add(serverNode);
			}
		}
	}

	public HashMap<String, Double> getServerLoad(double currentTime) {
		HashMap<String, Double> mac2Load = new HashMap<String, Double>();
		for (VNode vnode : table) {
			if (mac2Load.containsKey(vnode.macAddress)) {
				mac2Load.put(vnode.macAddress, vnode.getLoad(currentTime)
						+ mac2Load.get(vnode.macAddress));
			} else {
				mac2Load.put(vnode.macAddress, vnode.getLoad(currentTime));
			}
		}
		return mac2Load;
	}

	/*
	 * public String getMaxLoadServer(double currentTime) { HashMap<String,
	 * Double> mac2Load = new HashMap<String, Double>(); for (VNode vnode :
	 * table) { if (mac2Load.containsKey(vnode.macAddress)) {
	 * mac2Load.put(vnode.macAddress, vnode.getLoad(currentTime) +
	 * mac2Load.get(vnode.macAddress)); } else { mac2Load.put(vnode.macAddress,
	 * vnode.getLoad(currentTime)); } } String maxMacAddress = null; double
	 * maxLoad = Double.MIN_VALUE; for (String macAddress : mac2Load.keySet()) {
	 * if (mac2Load.get(macAddress) > maxLoad) { maxMacAddress = macAddress;
	 * maxLoad = mac2Load.get(macAddress); } } return maxMacAddress; }
	 */

	/*
	 * public void getMinMaxLoadVNode(Vector<VNode> nodes, double currentTime) {
	 * VNode minNode = null; VNode maxNode = null; for (VNode vnode : table) {
	 * if (minNode == null) { minNode = vnode; } else if
	 * (minNode.getLoad(currentTime) > vnode .getLoad(currentTime)) { minNode =
	 * vnode; } if (maxNode == null) { maxNode = vnode; } else if
	 * (maxNode.getLoad(currentTime) < vnode .getLoad(currentTime)) { maxNode =
	 * vnode; } } nodes.add(minNode); nodes.add(maxNode); }
	 */

	public void dump() {
		int totalRead = 0, totalWrite = 0;
		for (VNode vnode : table) {
			totalRead += vnode.readCounter;
			totalWrite += vnode.writeCounter;
		}
		System.out.println(totalRead + " " + totalWrite);
	}

	public synchronized Object clone() {
		try {
			RouteTable o = (RouteTable) super.clone();
			if (table != null) {
				o.table = new TreeSet<VNode>();
				for (VNode vnode : table) {
					o.table.add((VNode) vnode.clone());
				}
			}
			return o;
		} catch (CloneNotSupportedException e) {
			throw new InternalError();
		}
	}

}
